/* File: Magnify.m - Application class for 'Magnify'
 *
 * By: Christopher Lane
 * Symbolic Systems Resources Group
 * Knowledge Systems Laboratory
 * Stanford University
 *
 * Date:  9 November 1992
 *
 * Copyright: 1990, 1991 & 1992 by The Leland Stanford Junior University.
 * This program may be distributed without restriction for non-commercial use.
 */

#import <ctype.h>
#import <appkit/appkit.h>

#import "Magnify.h"
#import "MagnifyView.h"
#import "DefaultsTable.h"

#define WINDOWTITLEFORMAT "%.2gx Magnification"
#define WINDOWTITLEPROTOTYPE "0.00x Magnification"

#define DEFAULTSFILE "Defaults"

extern void timer(DPSTimedEntry, double, void *);

@implementation Magnify : Application

+ new
{
	char pathnamebuf[MAXPATHLEN];

	self = [super new];

	bundle = [NXBundle bundleForClass:[self class]];
	
	if ([bundle getPath:pathnamebuf forResource:DEFAULTSFILE ofType:"strings"])

	[(defaults = [[DefaultsTable alloc] initFromFile:pathnamebuf]) registerDefaults:[self appName]];

	timedEntry = NULL;

	return self;
}

- appDidInit:sender
{	
	MenuCell *cell;
	NXPoint *mouse;
	double scale, grid, update;
	Menu *menu = [self mainMenu];
	Window *window = [view window];

	scale = getFloatDefault("Scale");
	
	grid = getFloatDefault("GridSize");
	[gridButton setIntValue:getBoolDefault("ShowGrid")];

	update = getFloatDefault("UpdateInterval");
	[updateButton setIntValue:getBoolDefault("AutoUpdate")];
	
	[[[self showScale:scale andSlider:YES] showGrid:grid andSlider:YES] showUpdate:update andSlider:YES];
		
	[cursorButton setIntValue:getBoolDefault("ShowCursor")];

	[view setNextResponder:[window firstResponder]];

	[[window makeFirstResponder:view] addToEventMask:(NX_MOUSEMOVEDMASK | NX_FLAGSCHANGED)];

	if ((cell = [menu findCellWithTag:FROZENMENUCELLTAG]) != nil) {
		[cell setUpdateAction:@selector(isFrozenUpdateAction:) forMenu:menu];
		[menu update];
		}
		
	[window getMouseLocation:(mouse = [view mouse])];
	[window convertBaseToScreen:mouse];
	[[view updateBitmap:mouse] display];
	
	[window makeKeyAndOrderFront:self];
	
	return self;
}

- appWillTerminate:sender
{
	if([scaleSlider tag]) (void) writeDefault("Scale", [scaleField stringValue]);
	
	if([gridButton tag]) (void) writeBoolDefault("ShowGrid", [gridButton intValue]);
	if([gridSlider tag]) (void) writeDefault("GridSize", [gridField stringValue]);
	
	if([updateButton tag]) (void) writeBoolDefault("AutoUpdate", [updateButton intValue]);
	if([updateSlider tag]) (void) writeDefault("UpdateInterval", [updateField stringValue]);
	
	if([cursorButton tag]) (void) writeBoolDefault("ShowCursor", [cursorButton intValue]);

	return self;
}

- hide:sender
{
	if (![view isFrozen]) [[view window] removeFromEventMask:NX_MOUSEMOVEDMASK];
	
	return [super hide:sender];
}

- unhide:sender
{
	if (![view isFrozen]) [[view window] addToEventMask:NX_MOUSEMOVEDMASK];
	
	return [super unhide:sender];
}

- free
{
	if (timedEntry != NULL) DPSRemoveTimedEntry(timedEntry);

	return [super free];
}

- adjust:sender
{
	id slider;
	float value;
	int flag = ([view flags] & NX_ALTERNATEMASK);
	
	value = [(slider = (flag) ? gridSlider : scaleSlider) floatValue] + [[sender selectedCell] tag];
	
	[slider setFloatValue:MAX(MIN(value, [(Slider *) slider maxValue]), [(Slider *) slider minValue])];
	
	return (flag) ? [self changeGrid:slider] : [self changeScale:slider];
}

- changeScale:sender
{
	float scale, value = rint([sender floatValue]); /* 'intValue' truncates */

	scale = (value < -1.0) ? 1.0 / (- value) : 2.0 + value; /* non-linear slider */
	
	[[view setScale:(scale = floor(scale * 100.0) / 100.0)] createWindows]; /* round to nearest .01 */
	
	[sender setTag:YES];

	return [self showScale:scale andSlider:NO];
}

- showScale:(float) scale andSlider:(BOOL) flag
{
	char title[] = WINDOWTITLEPROTOTYPE;
	
	if(flag) {
		float value = rint((scale < 1) ? - (1 / scale) : scale - 2.0);
	
		if([scaleSlider floatValue] != value) [scaleSlider setFloatValue:value];
		}

	if([scaleField floatValue] != scale) [scaleField setFloatValue:scale];
	
	sprintf(title, WINDOWTITLEFORMAT, scale);
	
	[[view window] setTitle:title];

	return self;
}

- changeGrid:sender
{
	float gridSize = rint([gridSlider floatValue]);
	
	[sender setTag:YES];

	[[[view setGridSize:gridSize] setShowGrid:[gridButton intValue]] display];

	return [self showGrid:gridSize andSlider:NO];
}

- showGrid:(float) grid andSlider:(BOOL) flag
{
	[gridField setFloatValue:grid];
	
	if(flag && [gridSlider floatValue] != grid) [gridSlider setFloatValue:grid];

	return self;
}

- changeUpdate:sender
{
	[sender setTag:YES];

	return [self showUpdate:rint([updateSlider floatValue] * 10.0) / 10.0 andSlider:NO]; /* round to nearest 0.1 */
}

- showUpdate:(double) update andSlider:(BOOL) flag
{
	if (timedEntry != NULL) DPSRemoveTimedEntry(timedEntry);

	if([updateButton intValue]) timedEntry = DPSAddTimedEntry(update, (DPSTimedEntryProc) &timer, (void *) view, NX_BASETHRESHOLD);
	else timedEntry = NULL;

	[updateField setFloatValue:update];
	
	if(flag && [updateSlider floatValue] != update) [updateSlider setFloatValue:update];
		
	return self;
}

- toggleFrozen:sender
{
	[view setFrozen:![view isFrozen]];
	
	[[self mainMenu] update];
	
	return self;
}

- toggleCursor:sender
{
	[view setShowCursor:[sender intValue]];
	
	[sender setTag:YES];

	return self;
}

- getView { return view; }
- setView:anObject { view = anObject; return self; }

- setVersion:anObject
{
	[(version = anObject) setStringValue:VERSION];
	
	return self;
}

- (int) msgVersion:(const char **) aString ok:(int *) flag
{
	*flag = ((*aString = [version stringValue]) != NULL);

	return SEND_SUCCESS;
}

- (BOOL) isFrozenUpdateAction:(MenuCell *) anObject
{
	BOOL flag = [[NXApp getView] isFrozen];
	
	if ([anObject tag] == flag) return NO;
	
	[[anObject setTag:(int) flag] setTitle:(flag) ? UNFROZENMENUTITLE : FROZENMENUTITLE];
	[anObject setKeyEquivalent:tolower([anObject title][0])];
	
	return YES;
}

@end
